package tab

import (
	"bytes"
	"errors"
	"fmt"
	"net/url"
	"path"
	"path/filepath"
	"strings"

	"gopkg.in/xmlpath.v2"

	"code.google.com/awwwe-browser/auralvm/conversation"
	"code.google.com/awwwe-browser/auralvm/feed"
	"code.google.com/awwwe-browser/auralvm/param"
	"code.google.com/awwwe-browser/auralvm/setting"
	"code.google.com/awwwe-browser/auralvm/voisfile"
	"code.google.com/awwwe-browser/data"
	"code.google.com/awwwe-browser/issues"
	"code.google.com/awwwe-browser/scrub"
	"code.google.com/awwwe-browser/scrub/htmlsoup"
	"code.google.com/awwwe-browser/slurp"
	"code.google.com/awwwe-browser/vois"
	"code.google.com/awwwe-browser/xpath"
)

var tabKeyer int = 0

// a physical container a web page.
type Tab struct {
	Key      int
	Title    string
	URL      string
	Opener   *conversation.Conversation
	TOC      *conversation.Conversation
	Contents [][]*conversation.Conversation
}

// BuildTab is a tab constructor.
func BuildTab(location string) (Tab, error) {
	var dirtymarkup []byte
	var contenttype slurp.ContentType
	var err error
	dirtymarkup, contenttype, err = slurp.SlurpResource(location, param.BrowserCookieJar)
	return buildTab(location, dirtymarkup, contenttype, err)
}

// buildTabGet constructs a tag for an http get.
func buildTabGet(location string, values url.Values) (Tab, error) {
	var dirtymarkup []byte
	var contenttype slurp.ContentType
	var err error
	dirtymarkup, contenttype, err = slurp.SlurpResourceGet(location, values, param.BrowserCookieJar)
	return buildTab(location, dirtymarkup, contenttype, err)
}

// buildTabPost constructs a tab for a http post.
func buildTabPost(location string, values url.Values) (Tab, error) {
	var dirtymarkup []byte
	var contenttype slurp.ContentType
	var err error
	dirtymarkup, contenttype, err = slurp.SlurpResourcePost(location, values, param.BrowserCookieJar)
	return buildTab(location, dirtymarkup, contenttype, err)
}

// buildTab constructs a tab.
func buildTab(location string, dirtymarkup []byte, contenttype slurp.ContentType, err1 error) (Tab, error) {
	var scrubber *scrub.Scrubber
	var tab Tab
	var err error
	var vdata, sitevdata *vois.VOISData
	var fname, fpath string
	if err1 == nil {
		switch contenttype {
		case slurp.ContentTypeHTML:
			scrubber = scrub.NewScrubber(dirtymarkup, location)
			scrubber.Scrub()
			return buildHTMLTab(location, scrubber)
		case slurp.ContentTypeXML:
			return tab, errors.New("xml files are not supported yet")
		case slurp.ContentTypeText:
			return tab, errors.New("xml files are not supported yet")
		case slurp.ContentTypeRSS:
			vdata, err = voisfile.GetVOISDataBytes(vois.RSSVOISMarkup)
			if err != nil {
				return tab, errors.New("i was not able to parse the rss voice markup.")
			}

			fname, err = voisfile.URLToVOISFileName(location)
			if err != nil {
				return tab, errors.New("i was not able to find the web site's voice markup file.")
			}
			fpath = path.Join(param.LocalVOISFolderPath, fname)
			sitevdata, err = voisfile.GetVOISData(fpath)
			if err != nil {
				return tab, errors.New("i was not able to parse the web site's voice markup.")
			}
			return buildFeedTab(location, dirtymarkup, vdata, sitevdata)
		case slurp.ContentTypeATOM:
			vdata, err = voisfile.GetVOISDataBytes(vois.ATOMVOISMarkup)
			if err != nil {
				return tab, errors.New("i was not able to parse the atom voice markup.")
			}
			fname, err = voisfile.URLToVOISFileName(location)
			if err != nil {
				return tab, errors.New("i was not able to find the web site's voice markup file.")
			}
			fpath = path.Join(param.LocalVOISFolderPath, fname)
			sitevdata, err = voisfile.GetVOISData(fpath)
			if err != nil {
				return tab, errors.New("i was not able to parse the web site's voice markup.")
			}
			return buildFeedTab(location, dirtymarkup, vdata, sitevdata)
		}
	}
	return tab, err1 // errors.New("i was not able to get the web page.")
}

// buildFeedTab constructs a feed tab.
func buildFeedTab(location string, dirtybytes []byte, vdata *vois.VOISData, sitevdata *vois.VOISData) (Tab, error) {
	var err error
	var tab Tab
	var reader *bytes.Reader
	var tree *xmlpath.Node
	var content *vois.VOISContent
	var conversations []*conversation.Conversation
	var conv *conversation.Conversation
	var ok bool
	var option conversation.APIOption
	var i int
	var tocindex string
	var tabkeystring string
	var splits []string
	// build the xmlpath tree
	reader = bytes.NewReader(dirtybytes)
	tree, err = xmlpath.Parse(reader)
	if err != nil {
		return tab, errors.New("there were to many errors in the syndication feed.")
	}
	// build the tab
	tab.Key = tabKeyer
	tabKeyer++
	tabkeystring = fmt.Sprintf("%d", tab.Key)
	tab.URL = location
	tab.Contents = make([][]*conversation.Conversation, 0, 5)
	// step 3: build the navigation of the current articles
	tocindex = fmt.Sprintf("%d", len(vdata.Content))
	content = vdata.Content[0]
	conversations, ok = _buildFeedContentConversation(sitevdata, tree, content, tab.Key, 0)
	if ok {
		conv = conversations[0]
		conv.CallHelp.Package = param.Package
		conv.CallHelp.Object = "tab"
		conv.CallHelp.Method = "listen"
		conv.CallHelp.Arguments[param.ParamTabKey] = tabkeystring
		conv.CallHelp.Arguments[param.ParamTabContentIndex] = tocindex
		if len(tab.Contents) == 0 {
			// step 4 build the opener
			tab.Opener = conversation.NewConversation()
			conv.CopyTo(tab.Opener)
			tab.Opener.Intro = data.EmptyString
			tab.Opener.Header.DeleteAll()
			tab.Opener.Header.Append(fmt.Sprintf("From the web site, %s, this is the syndication feed featuring %s", sitevdata.SiteName, conversations[0].Header.AtIndex(0)[7:]))
			tab.Title = fmt.Sprintf("From the web site, %s, the syndication feed featuring %s", sitevdata.SiteName, conversations[0].Header.AtIndex(0)[7:])
		}
		tab.Contents = append(tab.Contents, conversations)
	}
	// step 6 build the toc
	tab.TOC = conversation.NewConversation()
	tab.TOC.Header.Append("These are the sections in this syndication feed.")
	if setting.BrowserSettings.HelpLevel != setting.SettingHelpLevel3 {
		// a lot of help or a little help.
		// add helpful
		tab.TOC.Helpful = "I will say the name of each section. After I say the name of a section, say, yes, if you want to listen to that section."
	}
	if setting.BrowserSettings.HelpLevel == setting.SettingHelpLevel1 {
		// a lot of help
		// add more helpful
		tab.TOC.MoreHelpful = "You may say, yes, back, restart, finish, help, pause, or quit."
	}
	tab.TOC.Footer = "That is the end of the list of sections in this syndication feed."
	tab.TOC.Type = conversation.ConversationTypeOptions
	tab.TOC.CallHelp.Package = param.Package
	tab.TOC.CallHelp.Object = "tab"
	tab.TOC.CallHelp.Method = "listtolisten"
	for i, conversations = range tab.Contents {
		option = conversation.BuildAPIOption()
		//option.VXMLs.Append(conversations[0].Header.AtIndex(0)[7:])
		splits = strings.SplitAfterN(conversations[0].Header.AtIndex(0), " ", 3)
		option.VXMLs.Append(splits[2])
		option.CallYes.Package = param.Package
		option.CallYes.Object = "tab"
		option.CallYes.Method = "listen"
		option.CallYes.Arguments[param.ParamTabKey] = tabkeystring
		option.CallYes.Arguments[param.ParamTabContentIndex] = fmt.Sprintf("%d", i)
		tab.TOC.Options = append(tab.TOC.Options, option)
	}
	return tab, nil
}

// buildHTMLTab builds an html tab.
func buildHTMLTab(location string, scrubber *scrub.Scrubber) (Tab, error) {
	var name, path string
	var err error
	var vdata *vois.VOISData
	var tab Tab
	var reader *bytes.Reader
	var tree *xmlpath.Node
	var content *vois.VOISContent
	var contentindex int
	var conversations []*conversation.Conversation
	var conv *conversation.Conversation
	var firstarticleindex, firstvideosindex int
	var ok bool
	var option conversation.APIOption
	var i, j, length int
	var tocindex string
	var tabkeystring string
	var contentindexstring string
	var helpobject, helpmethod string
    defer issues.LogFlush()
	firstarticleindex, firstvideosindex = -1, -1
	helpobject = "tab"
	helpmethod = "listen"
	// step 1: get the vdata, tree
	//   vdata has the xmlpaths used to get ids from tree
	//   vdata mirrors the scrubber tree but is from a different package
	name, err = voisfile.URLToVOISFileName(location)
	path = filepath.Join(param.LocalVOISFolderPath, name)
	vdata, err = voisfile.GetVOISData(path)
	if err != nil {
		return tab, errors.New("i was unable to get the voice markup file.")
	}
	// build the tab
	tab.Key = tabKeyer
	tabKeyer++
	tabkeystring = fmt.Sprintf("%d", tab.Key)
	tab.URL = location
	tab.Contents = make([][]*conversation.Conversation, 0, 5)
	// build the xmlpath tree
	reader = bytes.NewReader(scrubber.Bytes())
	tree, err = xmlpath.ParseHTML(reader)
	if err != nil {
		return tab, errors.New("there were to many errors in the web page.")
	}
	// step 2: set sayasats
	_setSayasAt(scrubber, tree, vdata)
	// step 3: build each content
	tocindex = fmt.Sprintf("%d", len(vdata.Content))
	contentindex = 0
	for i, content = range vdata.Content {
		conversations, ok = _buildContentConversation(scrubber, tree, content, tab.Key, contentindex)
		if ok {
			conv = conversations[0]
            if conv.Type == conversation.ConversationTypeTexts {
            	issues.Logger.Printf("conv.OpenWithVideo is %v.", content.OpenWithVideo)
                conv.OpenWithVideo = content.OpenWithVideo
            }
			conv.CallHelp.Package = param.Package
			conv.CallHelp.Object = helpobject
			conv.CallHelp.Method = helpmethod
			conv.CallHelp.Arguments[param.ParamTabKey] = tabkeystring
			conv.CallHelp.Arguments[param.ParamTabContentIndex] = tocindex
			if len(tab.Contents) == 0 {
				// step 4 build the opener
				tab.Opener = conversation.NewConversation()
				conv.CopyTo(tab.Opener)
				tab.Opener.Intro = data.EmptyString
				tab.Opener.Header.DeleteAll()
				tab.Opener.Header.Append(fmt.Sprintf("From the web site, %s, this is the web page featuring %s", vdata.SiteName, conversations[0].Header.AtIndex(0)[7:]))
				tab.Title = fmt.Sprintf("From the web site, %s, the web page featuring %s", vdata.SiteName, conversations[0].Header.AtIndex(0)[7:])
			}
			tab.Contents = append(tab.Contents, conversations)
			contentindex++
			if content.ContentType == vois.FormContentType {
				// set the call help for the form elements and the submit button.
				// help will restart the form.
				contentindexstring = fmt.Sprintf("%d", contentindex)
				length = len(conversations)
				for j = 1; j < length; j++ {
					conv = conversations[j]
					conv.CallHelp.Package = param.Package
					conv.CallHelp.Object = helpobject
					conv.CallHelp.Method = helpmethod
					conv.CallHelp.Arguments[param.ParamTabKey] = tabkeystring
					conv.CallHelp.Arguments[param.ParamTabContentIndex] = contentindexstring
					conv.CallHelp.Arguments[param.ParamTabContentContentIndex] = "0"
				}
			} else if content.ContentType == vois.ArticleContentType {
				// the article links
				conversations, ok = _buildContentConversationArticleLinks(scrubber, tree, content, tab.Key, contentindex)
				if ok {
					if firstarticleindex == -1 {
						firstarticleindex = i
					}
					conv = conversations[0]
					conv.CallHelp.Package = param.Package
					conv.CallHelp.Object = helpobject
					conv.CallHelp.Method = helpmethod
					conv.CallHelp.Arguments[param.ParamTabKey] = tabkeystring
					conv.CallHelp.Arguments[param.ParamTabContentIndex] = tocindex
					// set the call help for the form elements and the submit button.
					// restart the form.
					contentindexstring = fmt.Sprintf("%d", contentindex)
					length = len(conversations)
					for j = 1; j < length; j++ {
						conv = conversations[j]
						conv.CallHelp.Package = param.Package
						conv.CallHelp.Object = helpobject
						conv.CallHelp.Method = helpmethod
						conv.CallHelp.Arguments[param.ParamTabKey] = tabkeystring
						conv.CallHelp.Arguments[param.ParamTabContentIndex] = contentindexstring
						conv.CallHelp.Arguments[param.ParamTabContentContentIndex] = "0"
					}
					// add the article links conversation to the tab.
					tab.Contents = append(tab.Contents, conversations)
					contentindex++
				}
				// the article vids
				conversations, ok = _buildContentConversationArticleVideos(scrubber, tree, content, tab.Key, helpobject, helpmethod, contentindex)
				if ok {
					if firstarticleindex == i {
						firstvideosindex = contentindex
					}
					conv = conversations[0]
					conv.CallHelp.Package = param.Package
					conv.CallHelp.Object = helpobject
					conv.CallHelp.Method = helpmethod
					conv.CallHelp.Arguments[param.ParamTabKey] = tabkeystring
					conv.CallHelp.Arguments[param.ParamTabContentIndex] = tocindex
					// set the call help for the form elements and the submit button.
					// restart the form.
					contentindexstring = fmt.Sprintf("%d", contentindex)
					length = len(conversations)
					for j = 1; j < length; j++ {
						conv = conversations[j]
						conv.CallHelp.Package = param.Package
						conv.CallHelp.Object = helpobject
						conv.CallHelp.Method = helpmethod
						conv.CallHelp.Arguments[param.ParamTabKey] = tabkeystring
						conv.CallHelp.Arguments[param.ParamTabContentIndex] = contentindexstring
						conv.CallHelp.Arguments[param.ParamTabContentContentIndex] = "0"
					}
					// add the article vids conversation to the tab.
					tab.Contents = append(tab.Contents, conversations)
					contentindex++
				}
			}
		}
	}
    if len(tab.Contents) > 0 {
        // step 5 build the rss or atom feed subscription
        conversations, ok = buildConversationSubscription(location, scrubber, tree, vdata.AtomLinkAt, vdata.RSSLinkAt, tab.Key, contentindex)
        if ok {
            conv = conversations[0]
            conv.CallHelp.Package = param.Package
            conv.CallHelp.Object = helpobject
            conv.CallHelp.Method = helpmethod
            conv.CallHelp.Arguments[param.ParamTabKey] = tabkeystring
            conv.CallHelp.Arguments[param.ParamTabContentIndex] = tocindex
            conv.CallYes.Arguments[param.ParamSourceObject] = helpobject
            conv.CallYes.Arguments[param.ParamSourceMethod] = helpmethod
            conv.CallYes.Arguments[param.ParamSourceTabKey] = tabkeystring
            conv.CallYes.Arguments[param.ParamSourceContentIndex] = tocindex
            tab.Contents = append(tab.Contents, conversations)
        }
        // step 6 build the toc
        tab.TOC = conversation.NewConversation()
        tab.TOC.Header.Append("These are the sections in this web page.")
        if setting.BrowserSettings.HelpLevel != setting.SettingHelpLevel3 {
            // a lot of help or a little help.
            // add helpful
            tab.TOC.Helpful = "I will say the name of each section. After I say the name of a section, say, yes, if you want to listen to that section."
        }
        if setting.BrowserSettings.HelpLevel == setting.SettingHelpLevel1 {
            // a lot of help
            // add more helpful
            tab.TOC.MoreHelpful = "You may say, yes, back, restart, finish, help, pause, or quit."
        }
        tab.TOC.Footer = "That is the end of the list of sections in this web page."
        tab.TOC.Type = conversation.ConversationTypeOptions
        tab.TOC.CallHelp.Package = param.Package
        tab.TOC.CallHelp.Object = helpobject
        tab.TOC.CallHelp.Method = "listtolisten"
        for j, conversations = range tab.Contents {
        	issues.Logger.Printf("j is %d, len(conversations) is %d", j, len(conversations))
            option = conversation.BuildAPIOption()
            option.VXMLs.Append(conversations[0].Title)
            option.CallYes.Package = param.Package
            option.CallYes.Object = helpobject
            option.CallYes.Method = helpmethod
            option.CallYes.Arguments[param.ParamTabKey] = tabkeystring
            option.CallYes.Arguments[param.ParamTabContentIndex] = fmt.Sprintf("%d", j)
            tab.TOC.Options = append(tab.TOC.Options, option)
        }
		// step 7 start the tab with a video if required
        conversations = tab.Contents[0]
        conv = conversations[0]
        issues.Logger.Printf("conv.Type is %d && conv.IsMainContent is %v && conv.OpenWithVideo is %v", conv.Type, conv.IsMainContent, conv.OpenWithVideo)
        if conv.Type == conversation.ConversationTypeTexts && conv.IsMainContent && conv.OpenWithVideo {
            tab.Opener = _buildConversationArticleVideo(tab.Contents[firstarticleindex][0], tab.Contents[firstvideosindex][0])
            tab.Opener.CallHelp.Package = param.Package
            tab.Opener.CallHelp.Object = helpobject
            tab.Opener.CallHelp.Method = helpmethod
            tab.Opener.CallHelp.Arguments[param.ParamTabKey] = tabkeystring
            tab.Opener.CallHelp.Arguments[param.ParamTabContentIndex] = tocindex
        }
    }
	return tab, nil
}

// buildHTMLTab builds an html tab.
func oldbuildHTMLTab(location string, scrubber *scrub.Scrubber) (Tab, error) {
	var name, path string
	var err error
	var vdata *vois.VOISData
	var tab Tab
	var reader *bytes.Reader
	var tree *xmlpath.Node
	var content *vois.VOISContent
	var contentindex int
	var conversations []*conversation.Conversation
	var conv *conversation.Conversation
	var ok bool
	var option conversation.APIOption
	var i, length int
	var tocindex string
	var tabkeystring string
	var contentindexstring string
	var helpobject, helpmethod string
    defer issues.LogFlush()
	helpobject = "tab"
	helpmethod = "listen"
	// step 1: get the vdata, tree
	//   vdata has the xmlpaths used to get ids from tree
	//   vdata mirrors the scrubber tree but is from a different package
	name, err = voisfile.URLToVOISFileName(location)
	path = filepath.Join(param.LocalVOISFolderPath, name)
	vdata, err = voisfile.GetVOISData(path)
	if err != nil {
		return tab, errors.New("i was unable to get the voice markup file.")
	}
	// build the tab
	tab.Key = tabKeyer
	tabKeyer++
	tabkeystring = fmt.Sprintf("%d", tab.Key)
	tab.URL = location
	tab.Contents = make([][]*conversation.Conversation, 0, 5)
	// build the xmlpath tree
	reader = bytes.NewReader(scrubber.Bytes())
	tree, err = xmlpath.ParseHTML(reader)
	if err != nil {
		return tab, errors.New("there were to many errors in the web page.")
	}
	// step 2: set sayasats
	_setSayasAt(scrubber, tree, vdata)
	// step 3: build each content
	tocindex = fmt.Sprintf("%d", len(vdata.Content))
	contentindex = 0
	for _, content = range vdata.Content {
		conversations, ok = _buildContentConversation(scrubber, tree, content, tab.Key, contentindex)
		if ok {
			conv = conversations[0]
            issues.Logger.Print("article")
            if conv.Type == conversation.ConversationTypeTexts {
            	issues.Logger.Printf("conv.OpenWithVideo is %d.", content.OpenWithVideo)
                conv.OpenWithVideo = content.OpenWithVideo
            }
			conv.CallHelp.Package = param.Package
			conv.CallHelp.Object = helpobject
			conv.CallHelp.Method = helpmethod
			conv.CallHelp.Arguments[param.ParamTabKey] = tabkeystring
			conv.CallHelp.Arguments[param.ParamTabContentIndex] = tocindex
			if len(tab.Contents) == 0 {
				// step 4 build the opener
				tab.Opener = conversation.NewConversation()
				conv.CopyTo(tab.Opener)
				tab.Opener.Intro = data.EmptyString
				tab.Opener.Header.DeleteAll()
				tab.Opener.Header.Append(fmt.Sprintf("From the web site, %s, this is the web page featuring %s", vdata.SiteName, conversations[0].Header.AtIndex(0)[7:]))
				tab.Title = fmt.Sprintf("From the web site, %s, the web page featuring %s", vdata.SiteName, conversations[0].Header.AtIndex(0)[7:])
			}
			length = len(conversations)
			// set the call help for the form elements and the submit button.
			// restart the form.
			contentindexstring = fmt.Sprintf("%d", contentindex)
			for i = 1; i < length; i++ {
				conv = conversations[i]
				conv.CallHelp.Package = param.Package
				conv.CallHelp.Object = helpobject
				conv.CallHelp.Method = helpmethod
				conv.CallHelp.Arguments[param.ParamTabKey] = tabkeystring
				conv.CallHelp.Arguments[param.ParamTabContentIndex] = contentindexstring
				conv.CallHelp.Arguments[param.ParamTabContentContentIndex] = "0"
			}
			tab.Contents = append(tab.Contents, conversations)
			contentindex++
			if content.ContentType == vois.ArticleContentType {
				// add the article links
				conversations, ok = _buildContentConversationArticleLinks(scrubber, tree, content, tab.Key, contentindex)
				if ok {
					conv = conversations[0]
					conv.CallHelp.Package = param.Package
					conv.CallHelp.Object = helpobject
					conv.CallHelp.Method = helpmethod
					conv.CallHelp.Arguments[param.ParamTabKey] = tabkeystring
					conv.CallHelp.Arguments[param.ParamTabContentIndex] = tocindex
					length = len(conversations)
					// set the call help for the form elements and the submit button.
					// restart the form.
					contentindexstring = fmt.Sprintf("%d", contentindex)
					for i = 1; i < length; i++ {
						conv = conversations[i]
						conv.CallHelp.Package = param.Package
						conv.CallHelp.Object = helpobject
						conv.CallHelp.Method = helpmethod
						conv.CallHelp.Arguments[param.ParamTabKey] = tabkeystring
						conv.CallHelp.Arguments[param.ParamTabContentIndex] = contentindexstring
						conv.CallHelp.Arguments[param.ParamTabContentContentIndex] = "0"
					}
					tab.Contents = append(tab.Contents, conversations)
					contentindex++
				}
				// add the article vids
				conversations, ok = _buildContentConversationArticleVideos(scrubber, tree, content, tab.Key, helpobject, helpmethod, contentindex)
				if ok {
					conv = conversations[0]
					conv.CallHelp.Package = param.Package
					conv.CallHelp.Object = helpobject
					conv.CallHelp.Method = helpmethod
					conv.CallHelp.Arguments[param.ParamTabKey] = tabkeystring
					conv.CallHelp.Arguments[param.ParamTabContentIndex] = tocindex
					length = len(conversations)
					// set the call help for the form elements and the submit button.
					// restart the form.
					contentindexstring = fmt.Sprintf("%d", contentindex)
					for i = 1; i < length; i++ {
						conv = conversations[i]
						conv.CallHelp.Package = param.Package
						conv.CallHelp.Object = helpobject
						conv.CallHelp.Method = helpmethod
						conv.CallHelp.Arguments[param.ParamTabKey] = tabkeystring
						conv.CallHelp.Arguments[param.ParamTabContentIndex] = contentindexstring
						conv.CallHelp.Arguments[param.ParamTabContentContentIndex] = "0"
					}
					tab.Contents = append(tab.Contents, conversations)
					contentindex++
				}
			}
		}
	}
	// step 5 build the rss or atom feed subscription
	conversations, ok = buildConversationSubscription(location, scrubber, tree, vdata.AtomLinkAt, vdata.RSSLinkAt, tab.Key, contentindex)
	if ok {
		conv = conversations[0]
		conv.CallHelp.Package = param.Package
		conv.CallHelp.Object = helpobject
		conv.CallHelp.Method = helpmethod
		conv.CallHelp.Arguments[param.ParamTabKey] = tabkeystring
		conv.CallHelp.Arguments[param.ParamTabContentIndex] = tocindex
		conv.CallYes.Arguments[param.ParamSourceObject] = helpobject
		conv.CallYes.Arguments[param.ParamSourceMethod] = helpmethod
		conv.CallYes.Arguments[param.ParamSourceTabKey] = tabkeystring
		conv.CallYes.Arguments[param.ParamSourceContentIndex] = tocindex
		tab.Contents = append(tab.Contents, conversations)
	}
	// step 6 build the toc
	tab.TOC = conversation.NewConversation()
	tab.TOC.Header.Append("These are the sections in this web page.")
	if setting.BrowserSettings.HelpLevel != setting.SettingHelpLevel3 {
		// a lot of help or a little help.
		// add helpful
		tab.TOC.Helpful = "I will say the name of each section. After I say the name of a section, say, yes, if you want to listen to that section."
	}
	if setting.BrowserSettings.HelpLevel == setting.SettingHelpLevel1 {
		// a lot of help
		// add more helpful
		tab.TOC.MoreHelpful = "You may say, yes, back, restart, finish, help, pause, or quit."
	}
	tab.TOC.Footer = "That is the end of the list of sections in this web page."
	tab.TOC.Type = conversation.ConversationTypeOptions
	tab.TOC.CallHelp.Package = param.Package
	tab.TOC.CallHelp.Object = helpobject
	tab.TOC.CallHelp.Method = "listtolisten"
	for i, conversations = range tab.Contents {
		option = conversation.BuildAPIOption()
		option.VXMLs.Append(conversations[0].Title)
		option.CallYes.Package = param.Package
		option.CallYes.Object = helpobject
		option.CallYes.Method = helpmethod
		option.CallYes.Arguments[param.ParamTabKey] = tabkeystring
		option.CallYes.Arguments[param.ParamTabContentIndex] = fmt.Sprintf("%d", i)
		tab.TOC.Options = append(tab.TOC.Options, option)
	}
	return tab, nil
}

// buildSpeechLine builds a single string of text to speak to the user.
func buildSpeechLine(scrubber *scrub.Scrubber, tree *xmlpath.Node, onlyspeakat string) string {
	var nodes []*htmlsoup.SoupNode
	var node *htmlsoup.SoupNode
	var speech string
	var speeches []string
	speeches = make([]string, 0, 5)
	nodes = conversation.MatchXpathToTreeNodes(scrubber, tree, onlyspeakat)
	for _, node = range nodes {
		speech = node.VoiceString()
		// do translations
		speeches = append(speeches, speech)
	}
	speech = strings.Join(speeches, "")
	speech = strings.Replace(speech, "\r\n", "", -1)
	speech = strings.Replace(speech, "\r", "", -1)
	speech = strings.Replace(speech, "\n", "", -1)
	return strings.Trim(speech, data.WhiteSpaceString)
}

// buildConversationSubscription builds a conversation for a tab which lets the user subscribe to the feed.
func buildConversationSubscription(location string, scrubber *scrub.Scrubber, tree *xmlpath.Node, atomlinkat, rsslinkat string, tabkey, contentindex int) ([]*conversation.Conversation, bool) {
	var conversations []*conversation.Conversation
	var conv *conversation.Conversation
	var ok bool
	var nodes []*htmlsoup.SoupNode
	var href, ctype string
	var slicefeed feed.SliceFeed
	var fd *feed.Feed
	var i, length int
	var surl *url.URL
	var host *string
	var err error
	conv = conversation.NewConversation()
	conversations = make([]*conversation.Conversation, 0, 1)
	href = data.EmptyString
	nodes = conversation.MatchXpathToTreeNodes(scrubber, tree, atomlinkat)
	if len(nodes) > 0 {
		_, _, href = nodes[0].GetAttribute("href")
		if len(href) > 0 {
			// get the content type
			// needed for feedburner
			ctype = `application/atom+xml`
		}
	}
	if len(href) == 0 {
		nodes = conversation.MatchXpathToTreeNodes(scrubber, tree, rsslinkat)
		if len(nodes) > 0 {
			_, _, href = nodes[0].GetAttribute("href")
			if len(href) > 0 {
				// get the content type
				// needed for feedburner
				ctype = `application/rss+xml`
			}
		}
	}
	_ = ctype
	ok = len(href) > 0
	if ok {
		// check to see if there is a subscription to a feed for this site
		// if not then attempt to create the subscription conversation
		slicefeed = feed.UnMarshalFeeds()
		surl, err = url.Parse(location)
		ok = err == nil
		if ok {
			host = &surl.Host
			length = slicefeed.Len()
			for i = 0; i < length; i++ {
				fd = slicefeed.AtIndex(i)
				if *host == fd.Host {
					ok = false
					break
				}
			}
			if ok {
				// not currently subscribed to this web sites feed
				// this conversation is a yes no type
				// asking the user if he/she wants to subscribe to this web sites feed.
				conv = conversation.NewConversation()
				conv.Title = "the web site, syndication feed, subscription."
				conv.Type = conversation.ConversationTypeYesNo
				conv.Header.Append("This web site has a syndication feed which you are not subscribed to.")
				conv.Header.Append("would you like to subscribe to it?")
				if setting.BrowserSettings.HelpLevel != setting.SettingHelpLevel3 {
					conv.Helpful = "You may say, yes, no, help, pause, or quit."
				}
				conv.CallYes.Package = param.Package
				conv.CallYes.Object = "feed"
				conv.CallYes.Method = "add"
				conv.CallYes.Arguments[param.ParamFeedURL] = href
				conv.CallYes.Arguments[param.ParamFeedHost] = *host
				conversations = append(conversations, conv)
			}
		}
	}
	return conversations, ok
}

// _buildContentConversation initializes the data for tab contents
// builds the conversations for an article, nav, form, art links, art vids.
// based on content.Type
func _buildContentConversation(scrubber *scrub.Scrubber, tree *xmlpath.Node, content *vois.VOISContent, tabkey, contentindex int) ([]*conversation.Conversation, bool) {
	var conv *conversation.Conversation
	var conversations []*conversation.Conversation
	var line, title, ctype string
	var ok bool
	var formcontrols []*param.FormControl
	var i, length, last int
	var labels []string
	var sourceobject, sourcemethod string
	var sourcestartindex int
	var option conversation.APIOption
	var tabkeystring, contentindexstring, sourcestartindexstring string
	tabkeystring = fmt.Sprintf("%d", tabkey)
	contentindexstring = fmt.Sprintf("%d", contentindex)
	sourcestartindexstring = fmt.Sprintf("%d", sourcestartindex)
	sourceobject = "tab"
	sourcemethod = "listen"
	conv = conversation.NewConversation()
	conversations = make([]*conversation.Conversation, 0, 1)
	ok = xpath.IsTrueString(content.Condition, tree)
	if ok {
		// never speak at
		if scrubber != nil {
			_setNeverSpeakAt(scrubber, tree, content.NeverSpeakAt)
		}
		// title
		if len(content.TitleAt) > 0 {
			line = param.EndSentence(buildSpeechLine(scrubber, tree, content.TitleAt))
			line = strings.Replace(line, data.VXMLLongPauseString, "", -1)
			line = strings.Trim(line, data.WhiteSpaceString)
			if len(line) > 0 {
				title = line
			} else if len(content.Title) > 0 {
				title = content.Title
			}
		} else {
			title = content.Title
		}
		ok = len(title) > 0
		if ok {
			ctype = content.TypeToVoice()
			if content.ContentType == vois.AtomContentType || content.ContentType == vois.RSSContentType {
				conv.Title = "the web site syndication feed."
				conv.Header.Append("These are the links in the web site syndication feed.")
				conv.Footer = "That is the end of the links in the web site syndication feed."
			} else {
				conv.Title = fmt.Sprintf("the %s titled %s", ctype, title)
				conv.Header.Append(fmt.Sprintf("This is the %s titled %s", ctype, title))
				conv.Footer = fmt.Sprintf("That is the end of the %s titled %s", ctype, title)
			}
			// is main content
			conv.IsMainContent = content.IsMainContent
			// helfuls and content
			switch content.ContentType {
			case vois.ArticleContentType:
				// article, article links, article embeds.
				if setting.BrowserSettings.HelpLevel != setting.SettingHelpLevel3 {
					// a lot of help or a little help.
					// add helpful
					conv.Helpful = "I will speak the article to you. As I speak the article to you, say back, if you want me to go back and repeat a paragraph."
				}
				if setting.BrowserSettings.HelpLevel == setting.SettingHelpLevel1 {
					// a lot of help
					// add more helpful
					conv.MoreHelpful = "You may say, back, restart, finish, help, pause, or quit."
				}
				conv.Type = conversation.ConversationTypeTexts
				conv.BuildSpeechLines(scrubber, tree, content.OnlySpeakAt)
				if len(conv.Texts) > 0 {
					conversations = append(conversations, conv)
				}
			case vois.NavigationContentType:
				if setting.BrowserSettings.HelpLevel != setting.SettingHelpLevel3 {
					// a lot of help or a little help.
					// add helpful
					conv.Helpful = fmt.Sprintf("I will speak the links in the %s to you. After I speak a link to you, say yes, if you want to listen to the resource at that link. Or say, yes new tab, if you want to listen to the resource at that link in a new tab.", ctype)
				}
				if setting.BrowserSettings.HelpLevel == setting.SettingHelpLevel1 {
					// a lot of help
					// add more helpful
					conv.MoreHelpful = "You may say, yes, yes new tab, back, restart, finish, help, pause, or quit."
				}
				conv.Type = conversation.ConversationTypeLinks
				conv.BuildSpeechLinks(scrubber, tree, content.OnlySpeakAt, tabkey, "tab", "listen", contentindex)
				if len(conv.Options) > 0 {
					conversations = append(conversations, conv)
				}
			case vois.FormContentType:
				formcontrols = getFormControls(scrubber, tree, content.OnlySpeakAt)
				length = len(formcontrols)
				if length > 0 {
					// at least one input and a submit button
					if setting.BrowserSettings.HelpLevel != setting.SettingHelpLevel3 {
						// a lot of help or a little help.
						// add helpful
						conv.Helpful = "I will describe each form input element to you and ask you if you want to edit the element. Say yes if you want to edit that form input element."
					}
					if setting.BrowserSettings.HelpLevel == setting.SettingHelpLevel1 {
						// a lot of help
						// add more helpful
						conv.MoreHelpful = "You may say, yes, back, restart, finish, help, pause, or quit."
					}
					conv.Type = conversation.ConversationTypeOptions
					conversations = append(conversations, conv)
					// now add each form element conversation
					//   while building the form options which are the element labels.
					last = length - 1
					labels = make([]string, length, length)
					for i = 0; i < length; i++ {
						// CallYes is when the user says yes to accept or submit.
						conv = buildFormElementConversation(formcontrols[i])
						conv.CallYes.Package = param.Package
						conv.CallYes.Object = sourceobject
						if i < last {
							conv.CallYes.Method = sourcemethod
						} else {
							conv.CallYes.Method = "submit"
							conv.CallNo.Arguments[param.ParamTabKey] = tabkeystring
							conv.CallNo.Arguments[param.ParamTabContentIndex] = contentindexstring
							conv.CallNo.Arguments[param.ParamTabContentContentIndex] = "0"
							// after the form is submitted continue with the web page toc.
							conv.CallYes.Arguments[param.ParamSourceObject] = sourceobject
							conv.CallYes.Arguments[param.ParamSourceMethod] = sourcemethod
							conv.CallYes.Arguments[param.ParamSourceTabKey] = tabkeystring
							conv.CallYes.Arguments[param.ParamSourceContentIndex] = "0"
							conv.CallYes.Arguments[param.ParamSourceOptionIndex] = "-1"
						}
						conv.CallYes.Arguments[param.ParamTabKey] = tabkeystring
						conv.CallYes.Arguments[param.ParamTabContentIndex] = contentindexstring
						conv.CallYes.Arguments[param.ParamTabContentContentIndex] = "0"
						conv.CallYes.Arguments[param.ParamTabContentOptionIndex] = fmt.Sprintf("%d", i+1)
						conversations = append(conversations, conv)
						labels[i] = conv.Header.AtIndex(0)[8:]
						// when the user is saving the view must add the following params
						//   for each value: add a value=urlescape(value) pair
					}
					// complete the first conversation by adding the options to it.
					// the options come from the .Header of the other conversations.
					length = len(labels)
					last = length - 1
					for i = 0; i < length; i++ {
						option = conversation.BuildAPIOption()
						if i == 0 {
							option.Intro = "Continuing now with the form. the first item in the form is "
						} else if i < last {
							option.Intro = "Continuing now with the form. the next item in the form is "
						} else {
							option.Intro = "Continuing now with the form. the last item in the form is "
						}
						option.VXMLs.Append(labels[i])
						// the link calls
						option.CallYes.Package = param.Package
						option.CallYes.Object = sourceobject
						option.CallYes.Method = sourcemethod
						option.CallYes.Arguments[param.ParamTabKey] = tabkeystring
						option.CallYes.Arguments[param.ParamTabContentIndex] = contentindexstring
						option.CallYes.Arguments[param.ParamTabContentContentIndex] = fmt.Sprintf("%d", i+1)
						option.CallYes.Arguments[param.ParamSourceObject] = sourceobject
						option.CallYes.Arguments[param.ParamSourceMethod] = sourcemethod
						option.CallYes.Arguments[param.ParamSourceTabKey] = tabkeystring
						if i < last {
							option.CallYes.Arguments[param.ParamSourceContentIndex] = sourcestartindexstring
							option.CallYes.Arguments[param.ParamSourceOptionIndex] = fmt.Sprintf("%d", i+1)
						} else {
							option.CallYes.Arguments[param.ParamSourceContentIndex] = fmt.Sprintf("%d", sourcestartindex+1)
							option.CallYes.Arguments[param.ParamSourceOptionIndex] = fmt.Sprintf("%d", -1)
						}
						conversations[0].Options = append(conversations[0].Options, option)
					}
				}
			}
		}
	}
	return conversations, ok
}

// _buildFeedContentConversation initializes the data for tab contents
func _buildFeedContentConversation(sitevdata *vois.VOISData, tree *xmlpath.Node, content *vois.VOISContent, tabkey, contentindex int) ([]*conversation.Conversation, bool) {
	var conv *conversation.Conversation
	var conversations []*conversation.Conversation
	var title, ctype string
	var ok bool
	conv = conversation.NewConversation()
	conversations = make([]*conversation.Conversation, 0, 1)
	ok = xpath.IsTrueString(content.Condition, tree)
	if ok {
		title = content.Title
		title = htmlsoup.MakeSpeakable(title)
		title = sitevdata.Translate(title)
		ctype = content.TypeToVoice()
		conv.Header.Append(fmt.Sprintf("This is the %s titled %s", ctype, title))
		conv.Footer = fmt.Sprintf("That is the end of the %s titled %s", ctype, title)
		// helfuls and content
		switch content.ContentType {
		case vois.AtomContentType:
			if setting.BrowserSettings.HelpLevel != setting.SettingHelpLevel3 {
				// a lot of help or a little help.
				// add helpful
				conv.Helpful = fmt.Sprintf("I will speak the links in the %s to you. After I speak a link to you, say yes, if you want to listen to the resource at that link. Or say, yes new tab, if you want to listen to the resource at that link in a new tab.", ctype)
			}
			if setting.BrowserSettings.HelpLevel == setting.SettingHelpLevel1 {
				// a lot of help
				// add more helpful
				conv.MoreHelpful = "You may say, yes, back, restart, finish, help, pause, or quit."
			}
			conv.Type = conversation.ConversationTypeLinks
			conv.IsMainContent = sitevdata.HasMainContent()
			conv.BuildAtomSpeechLinks(sitevdata, tree, content.OnlySpeakAt, tabkey, "tab", "listen", contentindex)
			if len(conv.Options) > 0 {
				conversations = append(conversations, conv)
			}
		case vois.RSSContentType:
			if setting.BrowserSettings.HelpLevel != setting.SettingHelpLevel3 {
				// a lot of help or a little help.
				// add helpful
				conv.Helpful = fmt.Sprintf("I will speak the links in the %s to you. After I speak a link to you, say yes, if you want to listen to the resource at that link. Or say, yes new tab, if you want to listen to the resource at that link in a new tab.", ctype)
			}
			if setting.BrowserSettings.HelpLevel == setting.SettingHelpLevel1 {
				// a lot of help
				// add more helpful
				conv.MoreHelpful = "You may say, yes, back, restart, finish, help, pause, or quit."
			}
			conv.Type = conversation.ConversationTypeLinks
			conv.IsMainContent = sitevdata.HasMainContent()
			conv.BuildRSSSpeechLinks(sitevdata, tree, content.OnlySpeakAt, tabkey, "tab", "listen", contentindex)
			if len(conv.Options) > 0 {
				conversations = append(conversations, conv)
			}
		}
	}
	return conversations, ok
}

// _setSayasAt sets how to say nodes in the scrubber
func _setSayasAt(scrubber *scrub.Scrubber, tree *xmlpath.Node, vdata *vois.VOISData) {
	var nodes []*htmlsoup.SoupNode
	var node *htmlsoup.SoupNode
	var sayasat vois.VOISSayasAt
	for _, sayasat = range vdata.SayasAt {
		nodes = conversation.MatchXpathToTreeNodes(scrubber, tree, sayasat.Xpath)
		for _, node = range nodes {
			node.AddVoiceAttribute(sayasat.Sayas)
		}
	}
}

// _setNeverSpeakAt sets the nodes that should not be spoken to not visible.
// nodes which are not visible are not spoken.
func _setNeverSpeakAt(scrubber *scrub.Scrubber, tree *xmlpath.Node, neverspeakat string) {
	var node *htmlsoup.SoupNode
	var nodes []*htmlsoup.SoupNode
	// reset the whole tree to visible
	scrubber.SetTreeVisibility(true)
	nodes = conversation.MatchXpathToTreeNodes(scrubber, tree, neverspeakat)
	for _, node = range nodes {
		// set these nodes to not visible
		node.SetVisibility(false)
	}
}

// HasContents return true there is main content in the tab.
func (this *Tab) HasContents() bool {
	var cc []*conversation.Conversation
	for _, cc = range this.Contents {
		if cc[0].IsMainContent {
			return true
		}
	}
	return false
}
